/* assemble.c VM bytecode assembler */

#include <windows.h>

#include "trie.c"

#define BAD_HANDLE(x) ((x == NULL) || (x == INVALID_HANDLE_VALUE))

#define TYPE_INVALID 0
#define TYPE_SIGN 1
#define TYPE_WORD 2

struct Trie g_Label;

BOOL GetLine(HANDLE hIn, LPSTR lpszBuffer, DWORD dwSize){
    DWORD dwRead;
    DWORD dwIndex;

    ReadFile(hIn, lpszBuffer, dwSize, &dwRead, NULL);
    for(dwIndex = 0; dwIndex < dwRead; dwIndex++){
        if(lpszBuffer[dwIndex] == '\r'){
            lpszBuffer[dwIndex] = '\0';
            if(dwIndex + 1 < dwRead && lpszBuffer[dwIndex + 1] == '\n'){
                SetFilePointer(hIn, dwIndex - dwRead + 2, NULL, FILE_CURRENT);
                return TRUE;
            }
            SetFilePointer(hIn, dwIndex - dwRead + 1, NULL, FILE_CURRENT);
            return TRUE;
        }
        if(lpszBuffer[dwIndex] == '\n'){
            lpszBuffer[dwIndex] = '\0';
            SetFilePointer(hIn, dwIndex - dwRead + 1, NULL, FILE_CURRENT);
            return TRUE;
        }
    }
    return dwRead ? TRUE : FALSE;
}

BYTE CharType(CHAR c){
    static BYTE bList[0x100] = {0};

    if(c == -1){
        DWORD dwIndex;
        LPCSTR lpszSeq;

        lpszSeq = "-_0123456789abcdefghijklmnopqrstuvwxyz";
        dwIndex = 0;
        while(lpszSeq[dwIndex])
            bList[(BYTE)lpszSeq[dwIndex++]] = 1;
        lpszSeq = ",;:";
        dwIndex = 0;
        while(lpszSeq[dwIndex])
            bList[(BYTE)lpszSeq[dwIndex++]] = 2;
        lpszSeq = "\t\b\r\n ";
        dwIndex = 0;
        while(lpszSeq[dwIndex])
            bList[(BYTE)lpszSeq[dwIndex++]] = 3;
        return 0;
    }
    return bList[(BYTE)c];
}

// 0 => error
// 1 => sign
// 2 => name or number
BYTE GetNextWord(LPCSTR lpszLine, LPSTR lpszWord, DWORD dwSize){
    static DWORD dwIndex;
    DWORD dwPos = 0;

    if(lpszLine == NULL){
        dwIndex = 0;
        return 0;
    }
    while(lpszLine[dwIndex]){
        switch (CharType(lpszLine[dwIndex])) {
        case 0:
            return TYPE_INVALID;

        case 1:
            if(dwPos < dwSize)
                lpszWord[dwPos++] = lpszLine[dwIndex];
            break;
        case 2:
            if(dwPos){
                if(dwPos < dwSize)
                    lpszWord[dwPos] = '\0';
                return TYPE_WORD;
            }
            if(dwSize)
                *lpszWord = lpszLine[dwIndex];
            if(dwSize > 1)
                lpszWord[1] = '\0';
            dwIndex++;
            return TYPE_SIGN;

        case 3:
            if(dwPos){
                if(dwPos < dwSize)
                    lpszWord[dwPos] = '\0';
                return TYPE_WORD;
            }
            break;
        }
        dwIndex++;
    }
    if(dwPos){
        if(dwPos < dwSize)
            lpszWord[dwPos] = '\0';
        return TYPE_WORD;
    }
    return 0;
}

DWORD ParseInteger(LPCSTR lpszStr){
    DWORD dwType = 10;
    DWORD dwIndex = 0;
    DWORD dwVal = 0;
    CHAR c;

    if(lpszStr[dwIndex] == '0'){
        dwIndex++;
        if(lpszStr[dwIndex] == 'x'){
            dwType = 16;
            dwIndex++;
        }else if(lpszStr[dwIndex] == 'b'){
            dwType = 2;
            dwIndex++;
        }else{
            dwType = 8;
        }
    }
    switch (dwType) {
    case 2:
        while((c = lpszStr[dwIndex++])){
            if(c == '0' || c == '1'){
                dwVal = (dwVal << 1) | (DWORD)(c - '0');
            }
        }
        break;
    case 8:
        while((c = lpszStr[dwIndex++])){
            if(c >= '0' && c <= '7'){
                dwVal = (dwVal << 3) | (DWORD)(c - '0');
            }
        }
        break;
    case 10:
        while((c = lpszStr[dwIndex++])){
            if(c >= '0' && c <= '9'){
                dwVal = dwVal * 10 + (DWORD)(c - '0');
            }
        }
        break;
    case 16:
        while((c = lpszStr[dwIndex++])){
            if(c >= '0' && c <= '9'){
                dwVal = (dwVal << 4) | (DWORD)(c - '0');
            }else if(c >= 'a' && c <= 'f'){
                dwVal = (dwVal << 4) | (DWORD)(c - 'a' + 10);
            }else if(c >= 'A' && c <= 'F'){
                dwVal = (dwVal << 4) | (DWORD)(c - 'A' + 10);
            }
        }
        break;
    }
    return dwVal;
}

void ins_db(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    DWORD dwType;
    DWORD dwVal;

    do{
        dwType = GetNextWord(lpszLine, lpszWord, 0x3F);
        if(dwType != TYPE_WORD)
            break;
        if(*lpszWord == '-')
            dwVal = -ParseInteger(lpszWord + 1);
        else
            dwVal = ParseInteger(lpszWord);
        WriteFile(hOut, &dwVal, 1, NULL, NULL);
        dwType = GetNextWord(lpszLine, lpszWord, 0x3F);
    }while(dwType == TYPE_SIGN && !strcmp(lpszWord, ","));
}

void ins_dw(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    DWORD dwType;
    DWORD dwVal;

    do{
        dwType = GetNextWord(lpszLine, lpszWord, 0x3F);
        if(dwType != TYPE_WORD)
            break;
        if(*lpszWord == '-')
            dwVal = -ParseInteger(lpszWord + 1);
        else
            dwVal = ParseInteger(lpszWord);
        WriteFile(hOut, &dwVal, 2, NULL, NULL);
        dwType = GetNextWord(lpszLine, lpszWord, 0x3F);
    }while(dwType == TYPE_SIGN && !strcmp(lpszWord, ","));
}
void ins_dd(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    DWORD dwType;
    DWORD dwVal;

    do{
        dwType = GetNextWord(lpszLine, lpszWord, 0x3F);
        if(dwType != TYPE_WORD)
            break;
        if(*lpszWord == '-')
            dwVal = -ParseInteger(lpszWord + 1);
        else
            dwVal = ParseInteger(lpszWord);
        WriteFile(hOut, &dwVal, 4, NULL, NULL);
        dwType = GetNextWord(lpszLine, lpszWord, 0x3F);
    }while(dwType == TYPE_SIGN && !strcmp(lpszWord, ","));
}

void ins_push(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    DWORD dwType;
    DWORD dwVal;

    dwType = GetNextWord(lpszLine, lpszWord, 0x3F);
    if(dwType != TYPE_WORD)
        return;
    if(*lpszWord == '-'){
        dwVal = ParseInteger(lpszWord + 1);
        if(dwVal <= 0x80){
            dwVal = -dwVal;
            WriteFile(hOut, "\x00", 1, NULL, NULL);
            WriteFile(hOut, &dwVal, 1, NULL, NULL);
        }else if(dwVal <= 0x8000){
            dwVal = -dwVal;
            WriteFile(hOut, "\x01", 1, NULL, NULL);
            WriteFile(hOut, &dwVal, 2, NULL, NULL);
        }else{
            dwVal = -dwVal;
            WriteFile(hOut, "\x02", 1, NULL, NULL);
            WriteFile(hOut, &dwVal, 4, NULL, NULL);
        }
    }else{
        dwVal = ParseInteger(lpszWord);
        if(dwVal < 0x80){
            WriteFile(hOut, "\x00", 1, NULL, NULL);
            WriteFile(hOut, &dwVal, 1, NULL, NULL);
        }else if(dwVal < 0x8000){
            WriteFile(hOut, "\x01", 1, NULL, NULL);
            WriteFile(hOut, &dwVal, 2, NULL, NULL);
        }else{
            WriteFile(hOut, "\x02", 1, NULL, NULL);
            WriteFile(hOut, &dwVal, 4, NULL, NULL);
        }
    }
}

void ins_load(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    DWORD dwType;
    DWORD dwVal;

    dwType = GetNextWord(lpszLine, lpszWord, 0x3F);
    if(dwType != TYPE_WORD)
        return;
    dwVal = ParseInteger(lpszWord);
    WriteFile(hOut, "\x03", 1, NULL, NULL);
    WriteFile(hOut, &dwVal, 1, NULL, NULL);
}

void ins_store(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    DWORD dwType;
    DWORD dwVal;

    dwType = GetNextWord(lpszLine, lpszWord, 0x3F);
    if(dwType != TYPE_WORD)
        return;
    dwVal = ParseInteger(lpszWord);
    WriteFile(hOut, "\x04", 1, NULL, NULL);
    WriteFile(hOut, &dwVal, 1, NULL, NULL);
}

void ins_sys(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    DWORD dwType;
    DWORD dwVal;

    dwType = GetNextWord(lpszLine, lpszWord, 0x3F);
    if(dwType != TYPE_WORD)
        return;
    dwVal = ParseInteger(lpszWord);
    WriteFile(hOut, "\x05", 1, NULL, NULL);
    WriteFile(hOut, &dwVal, 1, NULL, NULL);
}

void ins_jmp(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    DWORD dwType;
    DWORD dwVal;

    dwType = GetNextWord(lpszLine, lpszWord, 0x3F);
    if(dwType != TYPE_WORD)
        return;
    WriteFile(hOut, "\x06", 1, NULL, NULL);
    if(*lpszWord >= 'a' && *lpszWord <= 'z'){
        LPDWORD lpdwConfig;
        struct{
            DWORD dwIndex;
            LPVOID lpvNext;
        } *pItem, *pNew;
        LARGE_INTEGER nPosition;
        INT nOffset;

        nPosition.QuadPart = 0;
        SetFilePointerEx(hOut, nPosition, &nPosition, FILE_CURRENT);
        lpdwConfig = TrieInsert(&g_Label, lpszWord);
        switch (*lpdwConfig) {
        case 0:
            *lpdwConfig = 2;
            pItem = (LPVOID)(lpdwConfig + 1);
            pItem->dwIndex = nPosition.LowPart;
            pItem->lpvNext = NULL;
            WriteFile(hOut, "\x00", 1, NULL, NULL);
            break;
        case 1:
            nOffset = *(++lpdwConfig) - nPosition.LowPart - 1;
            //if(nOffset < -80) error;
            WriteFile(hOut, &nOffset, 1, NULL, NULL);
            break;
        case 2:
            lpdwConfig += 2;
            pNew = malloc(sizeof(*pNew));
            pNew->dwIndex = nPosition.LowPart;
            pNew->lpvNext = (LPVOID)(DWORD_PTR)*lpdwConfig;
            *lpdwConfig = (DWORD)(DWORD_PTR)pNew;
            WriteFile(hOut, "\x00", 1, NULL, NULL);
            break;
        }
        return;
    }
    if(*lpszWord == '-'){
        dwVal = -ParseInteger(lpszWord + 1);
    }else{
        dwVal = ParseInteger(lpszWord);
    }
    WriteFile(hOut, &dwVal, 1, NULL, NULL);
}

void ins_add(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    WriteFile(hOut, "\x07", 1, NULL, NULL);
}

void ins_sub(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    WriteFile(hOut, "\x08", 1, NULL, NULL);
}

void ins_mul(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    WriteFile(hOut, "\x09", 1, NULL, NULL);
}

void ins_div(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    WriteFile(hOut, "\x0A", 1, NULL, NULL);
}

void ins_rem(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    WriteFile(hOut, "\x0B", 1, NULL, NULL);
}

void ins_and(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    WriteFile(hOut, "\x0C", 1, NULL, NULL);
}

void ins_or(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    WriteFile(hOut, "\x0D", 1, NULL, NULL);
}

void ins_xor(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    WriteFile(hOut, "\x0E", 1, NULL, NULL);
}

void ins_not(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    WriteFile(hOut, "\x0F", 1, NULL, NULL);
}

void ins_inc(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    WriteFile(hOut, "\x10", 1, NULL, NULL);
}

void ins_dec(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    WriteFile(hOut, "\x11", 1, NULL, NULL);
}

void ins_addf(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    WriteFile(hOut, "\x12", 1, NULL, NULL);
}

void ins_subf(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    WriteFile(hOut, "\x13", 1, NULL, NULL);
}

void ins_mulf(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    WriteFile(hOut, "\x14", 1, NULL, NULL);
}

void ins_divf(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    WriteFile(hOut, "\x15", 1, NULL, NULL);
}

void ins_i2f(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    WriteFile(hOut, "\x16", 1, NULL, NULL);
}

void ins_f2i(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    WriteFile(hOut, "\x17", 1, NULL, NULL);
}

void ins_cmpe(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    WriteFile(hOut, "\x18", 1, NULL, NULL);
}

void ins_cmpl(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    WriteFile(hOut, "\x19", 1, NULL, NULL);
}

void ins_cmpg(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    WriteFile(hOut, "\x1A", 1, NULL, NULL);
}

void ins_cmpne(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    WriteFile(hOut, "\x1B", 1, NULL, NULL);
}

void ins_cmpnl(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    WriteFile(hOut, "\x1C", 1, NULL, NULL);
}

void ins_cmpng(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    WriteFile(hOut, "\x1D", 1, NULL, NULL);
}

void ins_dup(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    WriteFile(hOut, "\x1E", 1, NULL, NULL);
}

void ins_jcc(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    DWORD dwType;
    DWORD dwVal;

    dwType = GetNextWord(lpszLine, lpszWord, 0x3F);
    if(dwType != TYPE_WORD)
        return;
    WriteFile(hOut, "\x1F", 1, NULL, NULL);
    if(*lpszWord >= 'a' && *lpszWord <= 'z'){
        LPDWORD lpdwConfig;
        struct{
            DWORD dwIndex;
            LPVOID lpvNext;
        } *pItem, *pNew;
        LARGE_INTEGER nPosition;
        INT nOffset;

        nPosition.QuadPart = 0;
        SetFilePointerEx(hOut, nPosition, &nPosition, FILE_CURRENT);
        lpdwConfig = TrieInsert(&g_Label, lpszWord);
        switch (*lpdwConfig) {
        case 0:
            *lpdwConfig = 2;
            pItem = (LPVOID)(lpdwConfig + 1);
            pItem->dwIndex = nPosition.LowPart;
            pItem->lpvNext = NULL;
            WriteFile(hOut, "\x00", 1, NULL, NULL);
            break;
        case 1:
            nOffset = *(++lpdwConfig) - nPosition.LowPart - 1;
            //if(nOffset < -80) error;
            WriteFile(hOut, &nOffset, 1, NULL, NULL);
            break;
        case 2:
            lpdwConfig += 2;
            pNew = malloc(sizeof(*pNew));
            pNew->dwIndex = nPosition.LowPart;
            pNew->lpvNext = (LPVOID)(DWORD_PTR)*lpdwConfig;
            *lpdwConfig = (DWORD)(DWORD_PTR)pNew;
            WriteFile(hOut, "\x00", 1, NULL, NULL);
            break;
        }
        return;
    }
    if(*lpszWord == '-'){
        dwVal = -ParseInteger(lpszWord + 1);
    }else{
        dwVal = ParseInteger(lpszWord);
    }
    WriteFile(hOut, &dwVal, 1, NULL, NULL);
}

void ins_sqrtinv(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    WriteFile(hOut, "\x20", 1, NULL, NULL);
}

void ins_reflb(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    WriteFile(hOut, "\x21", 1, NULL, NULL);
}

void ins_reflw(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    WriteFile(hOut, "\x22", 1, NULL, NULL);
}

void ins_refld(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    WriteFile(hOut, "\x23", 1, NULL, NULL);
}

void ins_refsb(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    WriteFile(hOut, "\x24", 1, NULL, NULL);
}

void ins_refsw(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    WriteFile(hOut, "\x25", 1, NULL, NULL);
}

void ins_refsd(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    WriteFile(hOut, "\x26", 1, NULL, NULL);
}

void ins_loadip(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    DWORD dwType;
    DWORD dwVal;

    dwType = GetNextWord(lpszLine, lpszWord, 0x3F);
    if(dwType != TYPE_WORD)
        return;
    WriteFile(hOut, "\x27", 1, NULL, NULL);
    if(*lpszWord >= 'a' && *lpszWord <= 'z'){
        LPDWORD lpdwConfig;
        struct{
            DWORD dwIndex;
            LPVOID lpvNext;
        } *pItem, *pNew;
        LARGE_INTEGER nPosition;
        INT nOffset;

        nPosition.QuadPart = 0;
        SetFilePointerEx(hOut, nPosition, &nPosition, FILE_CURRENT);
        lpdwConfig = TrieInsert(&g_Label, lpszWord);
        switch (*lpdwConfig) {
        case 0:
            *lpdwConfig = 2;
            pItem = (LPVOID)(lpdwConfig + 1);
            pItem->dwIndex = nPosition.LowPart;
            pItem->lpvNext = NULL;
            WriteFile(hOut, "\x00", 1, NULL, NULL);
            break;
        case 1:
            nOffset = *(++lpdwConfig) - nPosition.LowPart - 1;
            //if(nOffset < -80) error;
            WriteFile(hOut, &nOffset, 1, NULL, NULL);
            break;
        case 2:
            lpdwConfig += 2;
            pNew = malloc(sizeof(*pNew));
            pNew->dwIndex = nPosition.LowPart;
            pNew->lpvNext = (LPVOID)(DWORD_PTR)*lpdwConfig;
            *lpdwConfig = (DWORD)(DWORD_PTR)pNew;
            WriteFile(hOut, "\x00", 1, NULL, NULL);
            break;
        }
        return;
    }
    if(*lpszWord == '-'){
        dwVal = -ParseInteger(lpszWord + 1);
    }else{
        dwVal = ParseInteger(lpszWord);
    }
    WriteFile(hOut, &dwVal, 1, NULL, NULL);
}

void ins_call(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    DWORD dwType;
    DWORD dwVal;

    dwType = GetNextWord(lpszLine, lpszWord, 0x3F);
    if(dwType != TYPE_WORD)
        return;
    WriteFile(hOut, "\x28", 1, NULL, NULL);
    dwVal = ParseInteger(lpszWord) * 4;//raise an exception when this number is greater or equal than 64
    WriteFile(hOut, &dwVal, 1, NULL, NULL);
    dwType = GetNextWord(lpszLine, lpszWord, 0x3F);
    if(dwType == TYPE_SIGN && !strcmp(lpszWord, ","))
        dwType = GetNextWord(lpszLine, lpszWord, 0x3F);
    if(dwType != TYPE_WORD)
        return;
    if(*lpszWord >= 'a' && *lpszWord <= 'z'){
        LPDWORD lpdwConfig;
        struct{
            DWORD dwIndex;
            LPVOID lpvNext;
        } *pItem, *pNew;
        LARGE_INTEGER nPosition;
        INT nOffset;

        nPosition.QuadPart = 0;
        SetFilePointerEx(hOut, nPosition, &nPosition, FILE_CURRENT);
        lpdwConfig = TrieInsert(&g_Label, lpszWord);
        switch (*lpdwConfig) {
        case 0:
            *lpdwConfig = 2;
            pItem = (LPVOID)(lpdwConfig + 1);
            pItem->dwIndex = nPosition.LowPart;
            pItem->lpvNext = NULL;
            WriteFile(hOut, "\x00", 1, NULL, NULL);
            break;
        case 1:
            nOffset = *(++lpdwConfig) - nPosition.LowPart - 1;
            //if(nOffset < -80) error;
            WriteFile(hOut, &nOffset, 1, NULL, NULL);
            break;
        case 2:
            lpdwConfig += 2;
            pNew = malloc(sizeof(*pNew));
            pNew->dwIndex = nPosition.LowPart;
            pNew->lpvNext = (LPVOID)(DWORD_PTR)*lpdwConfig;
            *lpdwConfig = (DWORD)(DWORD_PTR)pNew;
            WriteFile(hOut, "\x00", 1, NULL, NULL);
            break;
        }
        return;
    }
    if(*lpszWord == '-'){
        dwVal = -ParseInteger(lpszWord + 1);
    }else{
        dwVal = ParseInteger(lpszWord);
    }
    WriteFile(hOut, &dwVal, 1, NULL, NULL);
}

void ins_callf(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    DWORD dwType;
    DWORD dwVal;

    dwType = GetNextWord(lpszLine, lpszWord, 0x3F);
    if(dwType != TYPE_WORD)
        return;
    WriteFile(hOut, "\x29", 1, NULL, NULL);
    dwVal = ParseInteger(lpszWord) * 4;//raise an exception when this number is greater or equal than 64
    WriteFile(hOut, &dwVal, 1, NULL, NULL);
    dwType = GetNextWord(lpszLine, lpszWord, 0x3F);
    if(dwType == TYPE_SIGN && !strcmp(lpszWord, ","))
        dwType = GetNextWord(lpszLine, lpszWord, 0x3F);
    if(dwType != TYPE_WORD){
        WriteFile(hOut, "\x00", 1, NULL, NULL);
        return;
    }
    dwVal = ParseInteger(lpszWord);
    WriteFile(hOut, &dwVal, 1, NULL, NULL);
}

void ins_ret(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    DWORD dwType;
    DWORD dwVal;

    dwType = GetNextWord(lpszLine, lpszWord, 0x3F);
    WriteFile(hOut, "\x2A", 1, NULL, NULL);
    if(dwType != TYPE_WORD){
        WriteFile(hOut, "\x00", 1, NULL, NULL);
        return;
    }
    dwVal = ParseInteger(lpszWord) * 4;//raise an exception when this number is greater or equal than 64
    WriteFile(hOut, &dwVal, 1, NULL, NULL);
}

void ins_retn(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    WriteFile(hOut, "\x2B", 1, NULL, NULL);
}

void ins_pop(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    WriteFile(hOut, "\x2C", 1, NULL, NULL);
}

void ins_popn(LPCSTR lpszLine, LPSTR lpszWord, HANDLE hOut){
    DWORD dwType;
    DWORD dwVal;

    dwType = GetNextWord(lpszLine, lpszWord, 0x3F);
    WriteFile(hOut, "\x2D", 1, NULL, NULL);
    if(dwType != TYPE_WORD){
        WriteFile(hOut, "\x00", 1, NULL, NULL);
        return;
    }
    dwVal = ParseInteger(lpszWord) * 4;//raise an exception when this number is greater or equal than 64
    WriteFile(hOut, &dwVal, 1, NULL, NULL);
}

void Assemble(HANDLE hIn, HANDLE hOut){
    static CHAR lpszLine[0x100] = {[0xFF] = '\0'};
    static CHAR lpszWord[0x40] = {[0x3F] = '\0'};
    static CHAR lpszSign[0x10] = {[0xF] = '\0'};
    const static struct{
        LPCSTR lpszIns;
        void(*lpfnHandler)(LPCSTR, LPSTR, HANDLE);
    }ppIns[] = {
        {"db", ins_db},
        {"dw", ins_dw},
        {"dd", ins_dd},
        {"push", ins_push},
        {"load", ins_load},
        {"store", ins_store},
        {"sys", ins_sys},
        {"jmp", ins_jmp},
        {"add", ins_add},
        {"sub", ins_sub},
        {"mul", ins_mul},
        {"div", ins_div},
        {"rem", ins_rem},
        {"or", ins_or},
        {"and", ins_and},
        {"xor", ins_xor},
        {"not", ins_not},
        {"inc", ins_inc},
        {"dec", ins_dec},
        {"addf", ins_addf},
        {"subf", ins_subf},
        {"mulf", ins_mulf},
        {"divf", ins_divf},
        {"i2f", ins_i2f},
        {"f2i", ins_f2i},
        {"cmpe", ins_cmpe},
        {"cmpl", ins_cmpl},
        {"cmpg", ins_cmpg},
        {"cmpne", ins_cmpne},
        {"cmpnl", ins_cmpnl},
        {"cmpng", ins_cmpng},
        {"dup", ins_dup},
        {"jcc", ins_jcc},
        {"sqrtinv", ins_sqrtinv},
        {"reflb", ins_reflb},
        {"reflw", ins_reflw},
        {"refld", ins_refld},
        {"refsb", ins_refsb},
        {"refsw", ins_refsw},
        {"refsd", ins_refsd},
        {"loadip", ins_loadip},
        {"call", ins_call},
        {"callf", ins_callf},
        {"ret", ins_ret},
        {"retn", ins_retn},
        {"pop", ins_pop},
        {"popn", ins_popn},
    };
    LARGE_INTEGER nPosition;
    struct Trie Instruction;
    DWORD dwIndex;

    Instruction = TrieCreate();
    g_Label = TrieCreate();
    CharType(-1);
    for(dwIndex = 0; dwIndex < (sizeof(ppIns) / sizeof(*ppIns)); dwIndex++)
        *((void(**)(LPCSTR, LPSTR, HANDLE))TrieInsert(&Instruction, ppIns[dwIndex].lpszIns)) = ppIns[dwIndex].lpfnHandler;
    while(GetLine(hIn, lpszLine, 0xFF)){
        DWORD dwType;
        LPDWORD lpdwConfig;
        void(**ppHandler)(LPCSTR, LPSTR, HANDLE);

        GetNextWord(NULL, NULL, 0);
        dwType = GetNextWord(lpszLine, lpszWord, 0x3F);
        if(dwType != TYPE_WORD)
            continue;
        ppHandler = TrieQuery(&Instruction, lpszWord);
        if(ppHandler){
            (*ppHandler)(lpszLine, lpszWord, hOut);
            goto next;
        }
        dwType = GetNextWord(lpszLine, lpszSign, 0xF);
        if(dwType != TYPE_SIGN || strcmp(lpszSign, ":"))
            continue;
        nPosition.QuadPart = 0;
        SetFilePointerEx(hOut, nPosition, &nPosition, FILE_CURRENT);
        lpdwConfig = TrieInsert(&g_Label, lpszWord);
        if(*lpdwConfig == 2){
            struct{
                DWORD dwIndex;
                LPVOID lpvNext;
            } *pItem = (LPVOID)(lpdwConfig + 1);
            while(pItem){
                LPVOID lpvCurrent = pItem;
                INT nOffset;

                SetFilePointer(hOut, pItem->dwIndex, NULL, FILE_BEGIN);
                nOffset = nPosition.LowPart - pItem->dwIndex - 1;
                //if(nOffset >= 80) error;
                WriteFile(hOut, &nOffset, 1, NULL, NULL);
                pItem = pItem->lpvNext;
                free(lpvCurrent);
            }
            SetFilePointer(hOut, nPosition.LowPart, NULL, FILE_BEGIN);
        }
        *(lpdwConfig++) = 1;
        *lpdwConfig = nPosition.LowPart;
        next:;
    }
    TrieDelete(&Instruction);
    TrieDelete(&g_Label);
}

int main(int argc, char** argv){
    HANDLE hOut;
    HANDLE hFile;
    HANDLE hOutFile;

    hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    if(argc != 3){
        WriteFile(hOut, "Usage: assemble.exe [file] [out]\r\n", 34, NULL, NULL);
        ExitProcess(1);
    }
    hFile = CreateFileA(
        argv[1],
        FILE_GENERIC_READ,
        0,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL
    );
    if(BAD_HANDLE(hFile)){
        WriteFile(hOut, "fatal error: Cannot open input file.\r\n", 38, NULL, NULL);
        ExitProcess(1);
    }
    hOutFile = CreateFileA(
        argv[2],
        FILE_GENERIC_WRITE,
        0,
        NULL,
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        NULL
    );
    if(BAD_HANDLE(hOutFile)){
        WriteFile(hOut, "fatal error: Cannot create output file.\r\n", 41, NULL, NULL);
        ExitProcess(1);
    }
    Assemble(hFile, hOutFile);
    CloseHandle(hFile);
    CloseHandle(hOutFile);
    ExitProcess(0);
}
